import os
import re
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from typing import List, Dict, Tuple, Optional
from docx import Document
from fpdf import FPDF
import threading
from queue import Queue
from docx.shared import Pt, RGBColor
from docx.oxml.ns import qn

class DocumentSplitter:
    """文档分块处理器，处理文本文档并分割成有意义的块"""
    
    def __init__(self, 
                min_chunk_size: int = 30, 
                max_chunk_size: int = 500,
                smart_merge: bool = True,
                respect_original_breaks: bool = True,
                max_heading_length: int = 50,
                short_chunk_threshold: int = 80,
                auto_number_headings: bool = False,
                preserve_numbering: bool = True):
        """初始化分块器
        
        Args:
            min_chunk_size: 最小块大小（字符数）
            max_chunk_size: 最大块大小（字符数）
            smart_merge: 是否智能合并小块
            respect_original_breaks: 是否尊重原始文档的分段
            max_heading_length: 最大标题长度
            short_chunk_threshold: 短块阈值，小于此值的块视为短块
            auto_number_headings: 是否自动为标题添加序号
            preserve_numbering: 是否在最终结果中保留添加的序号
        """
        self.min_chunk_size = min_chunk_size
        self.max_chunk_size = max_chunk_size
        self.smart_merge = smart_merge
        self.respect_original_breaks = respect_original_breaks
        self.max_heading_length = max_heading_length
        self.short_chunk_threshold = short_chunk_threshold
        self.auto_number_headings = auto_number_headings
        self.preserve_numbering = preserve_numbering
        
        # 缓存路径
        self.cache_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'cache')
        os.makedirs(self.cache_dir, exist_ok=True)
        
        # 初始化标识模式
        self._init_patterns()
        
    def _init_patterns(self):
        """初始化各种模式"""
        # 定义需要添加空行的分隔模式
        self.split_patterns = [
            r'^[\s]*第[一二三四五六七八九十百千]+章',  # 中文章节标题
            r'^[\s]*第\d+章',                       # 数字章节标题
            r'^[\s]*\d+[\s]*[\.、]',                # 数字编号开头
            r'^[\s]*[一二三四五六七八九十]+、',        # 中文数字编号
            r'^[\s]*[（\(][一二三四五六七八九十]+[）\)]', # 带括号的中文数字
            r'^[\s]*[（\(]\d+[）\)]',              # 带括号的数字
            r'^[\s]*【.*】',                        # 方括号标题
            r'^[\s]*※',                             # 特殊标记开头
            r'^[\s]*[a-zA-Z][\s]*$',               # 单独的字母
        ]
        
        # 标题模式
        self.heading_patterns = [
            r'^第[一二三四五六七八九十]+[章节]',  # 中文章节标题
            r'^第\d+[章节]',                   # 数字章节标题
            r'^\d+\.\d+\s+\S+',               # 数字小数点标题（如1.1 标题）
            r'^[\s]*[一二三四五六七八九十]+、\S+',      # 中文数字编号标题
            r'^\d+、\S+',                       # 数字编号标题
            r'^【.*】',                         # 方括号标题
            r'^※.*',                           # 特殊标记开头
            r'^[\u4e00-\u9fff]{1,10}[：:]',    # 中文冒号结尾短语
            r'^[\w\s\.]+[：:]',                 # 英文冒号结尾短语
        ]
        
        # 不应该分割的模式
        self.non_split_patterns = [
            r'^[\s]*[但而且并又]',       # 连接词开头
            r'^[\s]*[和与及]',           # 并列连接词
            r'^[\s]*[因为由于]',         # 原因连接词
        ]
        
        # 子项目模式（不独立成块的内容）
        self.sub_item_patterns = [
            r'^[\s]*[•▪·]',              # 项目符号开头
            r'^[\s]*[-—]',               # 破折号开头
            r'^[\s]*[a-zA-Z\d][\.、)]',   # 字母数字编号
        ]
        
        # 特殊前缀，表示是连续文本的一部分
        self.continuation_prefixes = [
            r'^[而但且并又][\s]',                # 连接词开头
            r'^[以因为由于如果][\s]',            # 连接词开头
        ]
        
        # 不完整内容的模式
        self.incomplete_patterns = [
            r'[^。！？\.!?]$',           # 不以句号感叹号问号结尾
            r'^[\s]*[,，:：][\s]*\S+',   # 以逗号冒号开头
        ]
        
        # 小标题模式（这些小标题可能需要与下方内容合并）
        self.heading_patterns = [
            r'^[\s]*[A-Z零一二三四五六七八九十]{1,3}[\s、.．:：,，)）]{1,2}[\s]*\S+',  # 短标题
            r'^[\s]*([一二三四五六七八九十]|[A-Z]|[0-9]{1,2})[\s]*[\.、．][\s]*\S+',  # 序号+短内容
            r'^[\s]*第[一二三四五六七八九十]{1,2}[章节条款项].*$',  # 章节标记
            r'^[\s]*\d+[\s]*$',  # 单独数字
            r'^[^，。！？；]+[：:][^：:]*$',  # 以冒号结尾的短句（关键修改）
            r'^[\w\s\.\d]+[：:][^：:]*$',  # 设备名称或功能名称后跟冒号（如"10.1寸集控器："）
            r'^[\u4e00-\u9fff]{1,10}(器|阀|管|机|泵|板|箱|系统|控制|模块)[：:]$',  # 中文设备名称后跟冒号
        ]
        
        # 可能是内容中断的模式，如果单独出现不应成块 
        self.incomplete_patterns = [
            r'^[\s]*[一二三四五六七八九十][\s]*$',  # 单独的中文数字
            r'^[\s]*[a-zA-Z][\s]*$',            # 单独的字母
            r'^[\s]*（.*）$',                    # 只有括号的内容
            r'^[\s]*\(.*\)$',                   # 只有括号的内容(英文)
        ]
        
        # 常见子项分隔模式（不需要前后空行，但内部处理时要考虑）
        self.sub_item_patterns = [
            r'^[•▪·]+[\s]*.*$',                 # 小点或小方块开头的行
            r'^-[\s]+.*$',                      # 短横线开头的行
            r'^[\s]{2,}.*$',                    # 缩进的内容
        ]
        
        # 不应分隔的模式（如果单独出现不会产生块分隔）
        self.non_split_patterns = [
            r'^[\s]*\d+[\s]*$',                 # 单独的数字
            r'^[\s]*[a-zA-Z][\s]*$',            # 单独的字母
        ]
        
        # 其他设置
        self.smart_merge = True                 # 智能合并小段落
        self.respect_original_breaks = True     # 尊重原始文档的段落分隔
        self.max_heading_length = 50            # 最大小标题长度
    
    def process_document(self, text: str) -> str:
        """主处理函数，调用各种处理方法"""
        # 1. 预处理文本（清理，规范化）
        cleaned_text = self._preprocess_text(text)
        
        # 2. 自动识别并添加标题序号（如果启用）
        if self.auto_number_headings:
            self.original_text = cleaned_text  # 保存原始文本以便后续还原
            cleaned_text = self.identify_and_number_headings(cleaned_text)
        
        # 3. 添加块间空行，识别原始分块
        structured_text = self._add_block_structure(cleaned_text)
        
        # 4. 合并小标题与内容 (智能合并小块)
        if self.smart_merge:
            structured_text = self._merge_small_chunks(structured_text)
        
        # 5. 最终处理和清理
        final_text = self._post_process(structured_text)
        
        # 6. 如果启用了自动标题序号但不保留序号，还原原始标题
        if self.auto_number_headings and not self.preserve_numbering:
            final_text = self._restore_original_headings(final_text)
        
        return final_text
    
    def _preprocess_text(self, text: str) -> str:
        """预处理文本，保留原始空行结构"""
        # 规范化换行符
        text = text.replace('\r\n', '\n').replace('\r', '\n')
        
        # 处理连续的空行为单个空行
        text = re.sub(r'\n{3,}', '\n\n', text)
        
        # 清理行首尾多余空格，但保留原始空行
        lines = []
        blank_line_count = 0
        
        for line in text.split('\n'):
            trimmed = line.strip()
            if not trimmed:
                if self.respect_original_breaks:
                    # 记录连续空行，但只保留一个
                    blank_line_count += 1
                    if blank_line_count <= 1:  # 最多保留一个空行
                        lines.append("")
            else:
                blank_line_count = 0
                lines.append(trimmed)
        
        return '\n'.join(lines)
    
    def _add_block_structure(self, text: str) -> str:
        """在适当位置添加空行分隔段落，尊重原始分块"""
        lines = text.split('\n')
        processed_lines = []
        current_chunk = []
        
        i = 0
        while i < len(lines):
            line = lines[i]
            
            # 处理空行 - 原始文档中的空行被视为分块标记
            if not line:
                if current_chunk:  # 如果当前有块在处理，保存它
                    processed_lines.append('\n'.join(current_chunk))
                    current_chunk = []
                    processed_lines.append('')  # 添加空行
                i += 1
                continue
            
            # 检测分块标记
            is_split_line = self._is_split_line(line)
            
            # 如果当前行是分块标记且当前块不为空，保存当前块并开始新块
            if is_split_line and current_chunk and not self._is_heading(line):
                processed_lines.append('\n'.join(current_chunk))
                processed_lines.append('')  # 添加空行
                current_chunk = []
            
            # 将当前行添加到当前块
            current_chunk.append(line)
            
            # 检查后续行是否应该合并到当前块
            j = i + 1
            while j < len(lines) and not self._should_split_after(lines[i], lines[j]):
                if lines[j].strip():  # 忽略空行
                    current_chunk.append(lines[j])
                j += 1
            
            # 如果有跳过的行，更新索引
            if j > i + 1:
                i = j
            else:
                i += 1
                
            # 如果当前块达到最大尺寸且不是标题行开头，保存当前块
            chunk_text = '\n'.join(current_chunk)
            if (self._get_chunk_length(chunk_text) > self.max_chunk_size and 
                not self._is_heading(current_chunk[0]) and len(current_chunk) > 1):
                # 寻找一个合适的分割点
                split_index = self._find_split_point(current_chunk)
                if split_index > 0:
                    processed_lines.append('\n'.join(current_chunk[:split_index]))
                    processed_lines.append('')  # 添加空行
                    current_chunk = current_chunk[split_index:]
        
        # 添加最后一个块
        if current_chunk:
            processed_lines.append('\n'.join(current_chunk))
        
        return '\n'.join(processed_lines)
    
    def _find_split_point(self, lines: List[str]) -> int:
        """在一组行中找到合适的分割点"""
        # 优先在句子结束处分割
        for i in range(len(lines) - 1, 0, -1):
            if re.search(r'[。！？.!?]$', lines[i-1]):
                return i
        
        # 如果没有找到句子结束，尝试在短行处分割
        for i in range(len(lines) - 1, 0, -1):
            if len(lines[i-1]) < 20:  # 短行
                return i
                
        # 如果都没找到，在中间分割
        return len(lines) // 2
    
    def _merge_small_chunks(self, text: str) -> str:
        """智能合并小标题与内容，特别是连续的小块内容"""
        chunks = text.split('\n\n')
        if len(chunks) <= 1:
            return text
            
        result_chunks = []
        i = 0
        
        while i < len(chunks):
            current_chunk = chunks[i]
            current_length = self._get_chunk_length(current_chunk)
            
            # 检查当前块是否是小标题或短块
            is_heading = self._is_small_heading(current_chunk)
            has_colon_ending = bool(re.search(r'[：:]\s*$', current_chunk))
            is_short_chunk = current_length < self.short_chunk_threshold
            
            # 如果当前块是小标题或短块，尝试连续合并后面的短块
            if (is_heading or has_colon_ending or is_short_chunk) and i + 1 < len(chunks):
                # 开始合并过程
                merged_chunk = current_chunk
                next_index = i + 1
                merged_count = 1
                
                # 持续合并后续的短块，直到遇到明确的新标题或较长的内容
                while next_index < len(chunks):
                    next_chunk = chunks[next_index]
                    next_is_heading = self._is_small_heading(next_chunk)
                    next_has_colon = bool(re.search(r'[：:]\s*$', next_chunk))
                    next_length = self._get_chunk_length(next_chunk)
                    
                    # 如果遇到明确的新标题且不是短块，停止合并
                    if (next_is_heading or next_has_colon) and next_length > self.short_chunk_threshold:
                        break
                        
                    # 如果累计合并的内容过长，也停止合并
                    if self._get_chunk_length(merged_chunk + '\n' + next_chunk) > self.max_chunk_size:
                        break
                    
                    # 合并这个块
                    merged_chunk = merged_chunk + '\n' + next_chunk
                    next_index += 1
                    merged_count += 1
                    
                    # 如果合并了一个长块，通常就可以停止了
                    if next_length > self.short_chunk_threshold * 2:
                        break
                
                # 添加合并后的块
                result_chunks.append(merged_chunk)
                i += merged_count
            else:
                # 不需要合并，直接添加当前块
                result_chunks.append(current_chunk)
                i += 1
        
        return '\n\n'.join(result_chunks)
    
    def _is_small_heading(self, text: str) -> bool:
        """判断文本是否为小标题"""
        if not text or '\n' in text:  # 包含多行的不是小标题
            return False
            
        # 长度检查 - 小标题通常较短
        if self._get_chunk_length(text) > self.max_heading_length:
            return False
        
        # 纯空行不是标题
        if not text.strip():
            return False
        
        # 特殊检查：以冒号结尾的短句很可能是小标题
        if re.search(r'[：:]\s*$', text) and len(text) < self.max_heading_length:
            return True
        
        # 常见中文小标题特征：内容短，2-10个字符后跟冒号
        if re.match(r'^[\u4e00-\u9fff]{2,10}[：:]$', text):
            return True
            
        # 检查是否匹配小标题模式
        return any(re.match(pattern, text) for pattern in self.heading_patterns)
    
    def _should_split_after(self, current_line: str, next_line: str) -> bool:
        """判断当前行后是否应该分块"""
        # 如果下一行是空行，应该分块
        if not next_line.strip():
            return True
            
        # 如果当前行是小标题且下一行不是另一个小标题，不分块
        # 这确保小标题会与后面的内容合并
        if (self._is_heading(current_line) or re.search(r'[：:]\s*$', current_line)) \
           and not self._is_heading(next_line) and not re.search(r'[：:]\s*$', next_line):
            return False
            
        # 如果下一行是明确的分块标志且不是内容的继续，应该分块
        if self._is_split_line(next_line) and not self._is_continuation(next_line):
            # 但如果下一行是以冒号结尾的标题，应该作为新块处理
            if re.search(r'[：:]\s*$', next_line):
                return True
            return True
            
        # 如果下一行是子项或连续内容，不分块
        if self._is_sub_item(next_line) or self._is_continuation(next_line):
            return False
            
        # 默认不分块
        return False
    
    def _is_heading(self, line: str) -> bool:
        """判断是否为标题行"""
        return (
            any(re.match(pattern, line) for pattern in self.heading_patterns) and
            self._get_chunk_length(line) <= self.max_heading_length
        )
    
    def _post_process(self, text: str) -> str:
        """最终处理和清理"""
        # 确保文档以换行符结束
        if not text.endswith('\n'):
            text += '\n'
            
        # 处理连续的空行为单个空行
        text = re.sub(r'\n{3,}', '\n\n', text)
        
        return text
    
    def _is_split_line(self, line: str) -> bool:
        """判断是否为块分隔行"""
        # 如果匹配任一分隔模式且不匹配非分隔模式
        return (
            any(re.match(pattern, line) for pattern in self.split_patterns) and 
            not any(re.match(pattern, line) for pattern in self.non_split_patterns)
        )
    
    def _is_sub_item(self, line: str) -> bool:
        """判断是否为子项（不单独成块，附属于前一个主项）"""
        return any(re.match(pattern, line) for pattern in self.sub_item_patterns)
    
    def _is_continuation(self, line: str) -> bool:
        """判断是否为前一行的连续内容"""
        # 检查是否以连接词开头
        return any(re.match(pattern, line) for pattern in self.continuation_prefixes)
    
    def _is_incomplete(self, line: str) -> bool:
        """判断是否为不完整内容"""
        return any(re.match(pattern, line) for pattern in self.incomplete_patterns)
    
    def _has_continuation_relation(self, chunk1: str, chunk2: str) -> bool:
        """检查两个块之间是否有明显的连续关系"""
        if not chunk1 or not chunk2:
            return False
            
        # 获取chunk2的第一行
        first_line = chunk2.split('\n')[0] if '\n' in chunk2 else chunk2
        
        # 检查是否以连接词开头
        return self._is_continuation(first_line)
    
    def _get_chunk_length(self, chunk: str) -> int:
        """获取块的有效字符长度（不计空格）"""
        return len(re.sub(r'\s', '', chunk))
        
    def add_empty_lines(self, text: str) -> str:
        """兼容旧接口"""
        return self.process_document(text)

    def identify_and_number_headings(self, text: str) -> str:
        """识别文档中的标题并添加序号"""
        lines = text.split('\n')
        processed_lines = []
        
        # 跟踪当前的标题层级和编号
        # 最多支持三级标题：如 1.2.3
        current_level = [0, 0, 0]  
        
        # 保存原始行到编号行的映射，用于后续还原
        self.heading_map = {}
        
        for line in lines:
            # 跳过空行
            if not line.strip():
                processed_lines.append(line)
                continue
                
            # 检测是否已有编号的标题
            already_numbered = self._has_heading_number(line)
            
            # 检测是否是未编号的标题
            is_heading = self._is_potential_heading(line)
            
            if already_numbered:
                # 已有编号的标题，检测其层级
                level = self._detect_heading_level(line)
                
                # 更新当前编号层级
                for i in range(level):
                    if i < len(current_level):
                        if i == level - 1:
                            # 获取当前编号值
                            number_match = re.search(r'\d+|[一二三四五六七八九十]+', line)
                            if number_match:
                                num_text = number_match.group()
                                # 转换可能的中文数字
                                if re.match(r'[一二三四五六七八九十]+', num_text):
                                    cn_number_map = {
                                        '一': 1, '二': 2, '三': 3, '四': 4, '五': 5,
                                        '六': 6, '七': 7, '八': 8, '九': 9, '十': 10
                                    }
                                    try:
                                        num_value = cn_number_map.get(num_text, 1)
                                        current_level[i] = num_value
                                    except:
                                        current_level[i] += 1
                                else:
                                    try:
                                        current_level[i] = int(num_text)
                                    except:
                                        current_level[i] += 1
                            else:
                                current_level[i] += 1
                        
                # 重置低级别编号
                for i in range(level, len(current_level)):
                    current_level[i] = 0
                
                processed_lines.append(line)
            elif is_heading:
                # 检测可能的标题层级
                potential_level = self._estimate_heading_level(line)
                
                # 更新当前编号
                current_level[potential_level-1] += 1
                # 重置低级别编号
                for i in range(potential_level, len(current_level)):
                    current_level[i] = 0
                    
                # 生成新编号
                if potential_level == 1:
                    new_number = f"{current_level[0]}"
                else:
                    new_number = '.'.join([str(n) for n in current_level[:potential_level] if n > 0])
                
                # 添加编号到标题
                numbered_line = f"{new_number}. {line.strip()}"
                
                # 存储原始行到编号后行的映射
                self.heading_map[numbered_line] = line
                
                processed_lines.append(numbered_line)
            else:
                # 普通内容
                processed_lines.append(line)
        
        return '\n'.join(processed_lines)
    
    def _has_heading_number(self, line: str) -> bool:
        """检查行是否已有标题序号"""
        patterns = [
            r'^[\s]*\d+\.',                    # 数字后跟点，如"1."
            r'^[\s]*\d+\.\d+',                 # 数字小数点数字，如"1.1"
            r'^[\s]*\d+\.\d+\.\d+',            # 多级数字，如"1.1.1"
            r'^[\s]*[一二三四五六七八九十]+[、．.]',  # 中文数字标记
            r'^[\s]*[A-Z][\.、]',               # 大写字母标记
            r'^[\s]*\([A-Z]\)',                # 括号中的大写字母
            r'^[\s]*（[一二三四五六七八九十]+）',    # 中文括号中的中文数字
        ]
        return any(re.match(pattern, line) for pattern in patterns)
    
    def _detect_heading_level(self, line: str) -> int:
        """检测已有编号标题的层级"""
        # 多级数字编号，如"1.2.3"
        if re.match(r'^[\s]*\d+\.\d+\.\d+', line):
            return 3
        # 二级数字编号，如"1.2"
        elif re.match(r'^[\s]*\d+\.\d+', line):
            return 2
        # 一级数字编号，如"1."
        elif re.match(r'^[\s]*\d+\.', line):
            return 1
        # 中文数字或字母编号，作为一级
        else:
            return 1
    
    def _is_potential_heading(self, line: str) -> bool:
        """检测可能是标题的行"""
        # 排除太长的行
        if len(line.strip()) > self.max_heading_length:
            return False
            
        # 排除以常见连接词开头的行
        if re.match(r'^[\s]*(而|但是|因此|所以|因为|由于|如果|虽然|尽管|然而|不过)', line):
            return False
        
        # 检查是否有明显的标题特征
        # 1. 特殊前缀词汇
        if re.match(r'^[\s]*(简介|概述|总结|结论|附录|说明|注意|背景|主要|关于|特点|功能|优势|用途|规格|特性|参数|型号|技术|规范)', line):
            return True
            
        # 2. 简短且不包含常见句末标点的独立短句
        if len(line.strip()) < 30 and not re.search(r'[,，。；]', line):
            # 以冒号结尾的短句很可能是标题
            if re.search(r'[：:]\s*$', line):
                return True
            # 不含常见句内标点的短句可能是标题
            if not re.search(r'[,，;；]', line):
                return True
        
        # 3. 匹配已定义的标题模式
        return any(re.match(pattern, line) for pattern in self.heading_patterns)
    
    def _estimate_heading_level(self, line: str) -> int:
        """估计标题的层级"""
        # 基于缩进、长度、特殊标记等估计层级
        indent = len(line) - len(line.lstrip())
        
        # 深度缩进，可能是三级标题
        if indent > 8:
            return 3
        # 明显缩进，可能是二级标题
        elif indent > 4:
            return 2
        
        # 长度很短的可能是一级标题
        if len(line.strip()) < 15:
            # 检查是否有明显的一级标题标记
            if re.match(r'^[\s]*(第[一二三四五六七八九十]+章|第\d+章|简介|概述|总结|结论)', line):
                return 1
            return 1
        # 中等长度可能是二级标题
        elif len(line.strip()) < 25:
            return 2
        # 默认作为二级标题
        else:
            return 2
    
    def _restore_original_headings(self, text: str) -> str:
        """还原原始标题（移除自动添加的序号）"""
        if not hasattr(self, 'heading_map') or not self.heading_map:
            return text
            
        result = text
        for numbered, original in self.heading_map.items():
            # 处理可能的换行和空格差异
            numbered_pattern = re.escape(numbered).replace(r'\ ', r'\s*')
            result = re.sub(numbered_pattern, original, result)
        
        return result

class DocumentProcessorApp:
    def __init__(self, root):
        self.root = root
        self.root.title("智能文档分块工具 v3.0")
        self.root.geometry("1100x700")
        self.root.minsize(900, 600)
        
        # 初始化变量
        self.current_file_path = ""
        self.processed_content = ""
        
        # 配置选项
        self.min_chunk_size = tk.IntVar(value=30)
        self.max_chunk_size = tk.IntVar(value=500)
        self.smart_merge = tk.BooleanVar(value=True)
        self.respect_breaks = tk.BooleanVar(value=True)
        self.max_heading_length = tk.IntVar(value=50)
        self.short_chunk_threshold = tk.IntVar(value=80)
        self.auto_number_headings = tk.BooleanVar(value=False)
        self.preserve_numbering = tk.BooleanVar(value=True)
        
        # 批量处理相关变量
        self.file_queue = Queue()
        self.processing = False
        self.current_file_index = 0
        self.total_files = 0
        self.all_results = {}  # {文件路径: 处理后的内容}
        
        # 初始化分词器
        self.splitter = DocumentSplitter()
        
        # 创建界面
        self.setup_style()
        self.create_widgets()
    
    def setup_style(self):
        """设置UI样式"""
        style = ttk.Style()
        # 使用元组形式的字体定义，确保格式正确
        default_font = ('Microsoft YaHei', 10)
        style.configure('TButton', font=default_font, padding=5)
        style.configure('TLabel', font=default_font)
        style.configure('TNotebook.Tab', font=default_font)
        style.configure('Horizontal.TProgressbar', thickness=20)
        style.configure('TCheckbutton', font=default_font)
        style.configure('TRadiobutton', font=default_font)
        style.configure('TEntry', font=default_font)
        
        # 设置全局字体选项
        self.root.option_add('*Font', default_font)
    
    def create_widgets(self):
        """创建GUI界面"""
        # 主框架
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 控制面板
        control_frame = ttk.Frame(main_frame)
        control_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 文件选择区域
        file_frame = ttk.Frame(control_frame)
        file_frame.pack(fill=tk.X, pady=5)
        
        self.open_btn = ttk.Button(
            file_frame, 
            text="选择文件", 
            command=self.open_file,
            style='TButton'
        )
        self.open_btn.pack(side=tk.LEFT, padx=5)
        
        self.batch_btn = ttk.Button(
            file_frame,
            text="批量选择",
            command=self.open_batch_files,
            style='TButton'
        )
        self.batch_btn.pack(side=tk.LEFT, padx=5)
        
        self.file_label = ttk.Label(file_frame, text="未选择文件")
        self.file_label.pack(side=tk.LEFT, padx=10)
        
        # 配置选项区域
        config_frame = ttk.LabelFrame(control_frame, text="分块配置")
        config_frame.pack(fill=tk.X, pady=5)
        
        # 配置选项的第一行
        config_row1 = ttk.Frame(config_frame)
        config_row1.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Label(config_row1, text="最小块字数:").pack(side=tk.LEFT, padx=5)
        ttk.Spinbox(
            config_row1, 
            from_=10, 
            to=200, 
            width=5, 
            textvariable=self.min_chunk_size
        ).pack(side=tk.LEFT, padx=5)
        
        ttk.Label(config_row1, text="最大块字数:").pack(side=tk.LEFT, padx=5)
        ttk.Spinbox(
            config_row1, 
            from_=100, 
            to=1000, 
            width=5, 
            textvariable=self.max_chunk_size
        ).pack(side=tk.LEFT, padx=5)
        
        ttk.Label(config_row1, text="最大标题长度:").pack(side=tk.LEFT, padx=5)
        ttk.Spinbox(
            config_row1, 
            from_=10, 
            to=100, 
            width=5, 
            textvariable=self.max_heading_length
        ).pack(side=tk.LEFT, padx=5)
        
        ttk.Label(config_row1, text="短块阈值:").pack(side=tk.LEFT, padx=5)
        ttk.Spinbox(
            config_row1, 
            from_=30, 
            to=200, 
            width=5, 
            textvariable=self.short_chunk_threshold
        ).pack(side=tk.LEFT, padx=5)
        
        # 配置选项的第二行
        config_row2 = ttk.Frame(config_frame)
        config_row2.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Checkbutton(
            config_row2,
            text="智能合并小标题与内容",
            variable=self.smart_merge
        ).pack(side=tk.LEFT, padx=5)
        
        ttk.Checkbutton(
            config_row2,
            text="尊重原始段落分隔",
            variable=self.respect_breaks
        ).pack(side=tk.LEFT, padx=15)
        
        # 添加配置选项的第三行
        config_row3 = ttk.Frame(config_frame)
        config_row3.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Checkbutton(
            config_row3,
            text="自动识别并添加标题序号",
            variable=self.auto_number_headings
        ).pack(side=tk.LEFT, padx=5)
        
        ttk.Checkbutton(
            config_row3,
            text="保留添加的序号",
            variable=self.preserve_numbering
        ).pack(side=tk.LEFT, padx=15)
        
        # 添加预览标题按钮
        self.preview_numbering_btn = ttk.Button(
            config_row3,
            text="预览标题识别效果",
            command=self.preview_heading_numbering,
            state=tk.DISABLED
        )
        self.preview_numbering_btn.pack(side=tk.LEFT, padx=5)
        
        ttk.Button(
            config_row2,
            text="应用配置",
            command=self.apply_config
        ).pack(side=tk.RIGHT, padx=5)
        
        # 处理按钮区域
        process_frame = ttk.Frame(control_frame)
        process_frame.pack(fill=tk.X, pady=5)
        
        self.process_btn = ttk.Button(
            process_frame,
            text="处理当前文件",
            command=self.process_current_file,
            style='TButton',
            state=tk.DISABLED
        )
        self.process_btn.pack(side=tk.LEFT, padx=5)
        
        self.batch_process_btn = ttk.Button(
            process_frame,
            text="批量处理",
            command=self.start_batch_process,
            style='TButton',
            state=tk.DISABLED
        )
        self.batch_process_btn.pack(side=tk.LEFT, padx=5)
        
        self.stop_btn = ttk.Button(
            process_frame,
            text="停止处理",
            command=self.stop_processing,
            style='TButton',
            state=tk.DISABLED
        )
        self.stop_btn.pack(side=tk.LEFT, padx=5)
        
        # 进度条
        self.progress = ttk.Progressbar(
            control_frame,
            orient='horizontal',
            mode='determinate',
            style='Horizontal.TProgressbar'
        )
        self.progress.pack(fill=tk.X, pady=5)
        self.progress_label = ttk.Label(control_frame, text="准备就绪")
        self.progress_label.pack()
        
        # 导出选项区域
        export_frame = ttk.Frame(control_frame)
        export_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(export_frame, text="导出格式:").pack(side=tk.LEFT, padx=(10, 5))
        
        self.export_format = tk.StringVar(value='docx')
        formats = [('Word文档', 'docx'), ('PDF文件', 'pdf'), ('文本文件', 'txt')]
        for text, value in formats:
            ttk.Radiobutton(
                export_frame, 
                text=text, 
                variable=self.export_format,
                value=value
            ).pack(side=tk.LEFT, padx=2)
        
        self.export_btn = ttk.Button(
            export_frame,
            text="导出当前文档",
            command=self.export_current_document,
            style='TButton',
            state=tk.DISABLED
        )
        self.export_btn.pack(side=tk.LEFT, padx=10)
        
        self.export_all_btn = ttk.Button(
            export_frame,
            text="导出全部结果",
            command=self.export_all_results,
            style='TButton',
            state=tk.DISABLED
        )
        self.export_all_btn.pack(side=tk.LEFT, padx=5)
        
        # 内容区域
        content_frame = ttk.Frame(main_frame)
        content_frame.pack(fill=tk.BOTH, expand=True)
        
        # 使用Notebook实现标签页
        self.notebook = ttk.Notebook(content_frame)
        self.notebook.pack(fill=tk.BOTH, expand=True)
        
        # 原始文档标签页
        original_tab = ttk.Frame(self.notebook)
        self.notebook.add(original_tab, text='原始文档')
        
        self.original_text = tk.Text(
            original_tab,
            wrap=tk.WORD,
            font=('Microsoft YaHei', 10),
            undo=True
        )
        original_scroll = ttk.Scrollbar(original_tab, command=self.original_text.yview)
        original_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.original_text.config(yscrollcommand=original_scroll.set)
        self.original_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 处理后文档标签页
        processed_tab = ttk.Frame(self.notebook)
        self.notebook.add(processed_tab, text='分块结果')
        
        self.processed_text = tk.Text(
            processed_tab,
            wrap=tk.WORD,
            font=('Microsoft YaHei', 10),
            undo=True
        )
        processed_scroll = ttk.Scrollbar(processed_tab, command=self.processed_text.yview)
        processed_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.processed_text.config(yscrollcommand=processed_scroll.set)
        self.processed_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 添加行号显示
        self._setup_line_numbers()
        
        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("准备就绪")
        
        status_bar = ttk.Frame(self.root, height=25)
        status_bar.pack(fill=tk.X, padx=10, pady=(0, 10))
        
        ttk.Label(
            status_bar,
            textvariable=self.status_var,
            relief=tk.SUNKEN,
            anchor=tk.W,
            font=('Microsoft YaHei', 9)
        ).pack(fill=tk.X)
    
    def _setup_line_numbers(self):
        """为文本区域设置行号显示"""
        # 可以根据需要实现行号显示
        pass
    
    def apply_config(self):
        """应用配置到分词器"""
        try:
            min_size = self.min_chunk_size.get()
            max_size = self.max_chunk_size.get()
            heading_length = self.max_heading_length.get()
            short_threshold = self.short_chunk_threshold.get()
            
            # 验证值
            if min_size < 10:
                min_size = 10
            if max_size < min_size:
                max_size = min_size + 100
            if heading_length < 10:
                heading_length = 10
            if short_threshold < min_size:
                short_threshold = min_size + 20
                
            # 更新配置
            self.splitter.min_chunk_size = min_size
            self.splitter.max_chunk_size = max_size
            self.splitter.max_heading_length = heading_length
            self.splitter.short_chunk_threshold = short_threshold
            self.splitter.smart_merge = self.smart_merge.get()
            self.splitter.respect_original_breaks = self.respect_breaks.get()
            self.splitter.auto_number_headings = self.auto_number_headings.get()
            self.splitter.preserve_numbering = self.preserve_numbering.get()
            
            # 构建状态消息
            status_msg = f"配置已更新: 最小块={min_size}, 最大块={max_size}, 标题长度={heading_length}, 短块阈值={short_threshold}"
            if self.auto_number_headings.get():
                status_msg += ", 自动添加标题序号"
                if self.preserve_numbering.get():
                    status_msg += "(保留序号)"
                else:
                    status_msg += "(仅用于分块)"
            
            self.status_var.set(status_msg)
            
            # 如果已有处理过的文件，提示可以重新处理
            if self.current_file_path:
                if messagebox.askyesno("提示", "配置已更新，是否重新处理当前文件？"):
                    self.process_current_file()
        except Exception as e:
            messagebox.showerror("错误", f"更新配置失败: {str(e)}")
            
    def open_file(self):
        """打开单个文件"""
        file_path = filedialog.askopenfilename(
            title="选择文档",
            filetypes=[
                ("Word文档", "*.docx"),
                ("文本文件", "*.txt"), 
                ("所有文件", "*.*")
            ]
        )
        
        if file_path:
            self.load_file(file_path)
    
    def open_batch_files(self):
        """批量选择文件"""
        file_paths = filedialog.askopenfilenames(
            title="选择多个文档",
            filetypes=[
                ("Word文档", "*.docx"),
                ("文本文件", "*.txt"), 
                ("所有文件", "*.*")
            ]
        )
        
        if file_paths:
            self.file_queue = Queue()
            for path in file_paths:
                self.file_queue.put(path)
            
            self.total_files = self.file_queue.qsize()
            self.current_file_index = 0
            
            if hasattr(self, 'batch_process_btn'):
                self.batch_process_btn.config(state=tk.NORMAL)
            if hasattr(self, 'file_label'):
                self.file_label.config(text=f"已选择 {self.total_files} 个文件")
            self.status_var.set(f"准备批量处理 {self.total_files} 个文件")
    
    def load_file(self, file_path):
        """加载文件内容"""
        self.current_file_path = file_path
        try:
            if file_path.endswith('.docx'):
                doc = Document(file_path)
                full_text = "\n".join([para.text for para in doc.paragraphs if para.text])
            else:
                with open(file_path, 'r', encoding='utf-8') as f:
                    full_text = f.read()
            
            self.original_text.delete(1.0, tk.END)
            self.original_text.insert(tk.END, full_text)
            
            # 启用处理按钮
            if hasattr(self, 'process_btn'):
                self.process_btn.config(state=tk.NORMAL)
            
            # 启用预览标题按钮    
            if hasattr(self, 'preview_numbering_btn'):
                self.preview_numbering_btn.config(state=tk.NORMAL)
                
            self.file_label.config(text=os.path.basename(file_path))
            self.status_var.set(f"已加载: {os.path.basename(file_path)}")
            
            # 清空之前的结果
            self.processed_text.delete(1.0, tk.END)
            
            # 禁用导出按钮，直到处理完成
            if hasattr(self, 'export_btn'):
                self.export_btn.config(state=tk.DISABLED)
            
        except Exception as e:
            messagebox.showerror("错误", f"无法读取文档:\n{str(e)}")
    
    def process_current_file(self):
        """处理当前显示的文件"""
        if not self.current_file_path:
            messagebox.showwarning("警告", "没有可处理的文件")
            return
        
        raw_text = self.original_text.get(1.0, tk.END)
        if not raw_text.strip():
            messagebox.showwarning("警告", "没有可处理的文档内容")
            return
        
        try:
            # 显示处理中状态
            self.status_var.set("正在处理文档...")
            self.progress_label.config(text="处理中...")
            self.root.update()
            
            # 处理文档
            self.processed_content = self.splitter.process_document(raw_text)
            
            # 保存结果
            self.all_results[self.current_file_path] = self.processed_content
            
            # 显示结果
            self.processed_text.delete(1.0, tk.END)
            self.processed_text.insert(tk.END, self.processed_content)
            
            # 启用导出按钮
            if hasattr(self, 'export_btn'):
                self.export_btn.config(state=tk.NORMAL)
            
            # 如果有多个文件，启用批量导出
            if len(self.all_results) > 1 and hasattr(self, 'export_all_btn'):
                self.export_all_btn.config(state=tk.NORMAL)
            
            # 切换到结果标签页
            self.notebook.select(1)
            
            # 计算分块数量
            chunks = self.processed_content.split('\n\n')
            chunk_count = len(chunks)
            
            self.status_var.set(f"处理完成: {os.path.basename(self.current_file_path)} - 共分成 {chunk_count} 个块")
            self.progress_label.config(text=f"处理完成 - {chunk_count} 个块")
        except Exception as e:
            messagebox.showerror("错误", f"文档处理失败:\n{str(e)}")
            self.status_var.set("处理失败")
            self.progress_label.config(text="处理失败")
    
    def start_batch_process(self):
        """开始批量处理文件"""
        if self.file_queue.empty():
            messagebox.showwarning("警告", "没有可处理的文件")
            return
        
        # 初始化进度
        self.total_files = self.file_queue.qsize()
        self.current_file_index = 0
        self.processing = True
        
        # 更新UI状态
        if hasattr(self, 'open_btn'):
            self.open_btn.config(state=tk.DISABLED)
        if hasattr(self, 'batch_btn'):
            self.batch_btn.config(state=tk.DISABLED)
        if hasattr(self, 'process_btn'):
            self.process_btn.config(state=tk.DISABLED)
        if hasattr(self, 'batch_process_btn'):
            self.batch_process_btn.config(state=tk.DISABLED)
        if hasattr(self, 'stop_btn'):
            self.stop_btn.config(state=tk.NORMAL)
        
        # 启动处理线程
        threading.Thread(target=self.batch_process_files, daemon=True).start()
    
    def batch_process_files(self):
        """批量处理文件的线程函数"""
        self.all_results = {}  # 清空之前的结果
        
        while not self.file_queue.empty() and self.processing:
            file_path = self.file_queue.get()
            self.current_file_index += 1
            
            # 更新进度
            progress = int((self.current_file_index / self.total_files) * 100)
            if hasattr(self, 'progress'):
                self.progress['value'] = progress
            self.progress_label.config(
                text=f"正在处理 {self.current_file_index}/{self.total_files}: {os.path.basename(file_path)}"
            )
            self.status_var.set(f"正在处理 {self.current_file_index}/{self.total_files}...")
            self.root.update()
            
            try:
                # 加载文件内容
                if file_path.endswith('.docx'):
                    doc = Document(file_path)
                    raw_text = "\n".join([para.text for para in doc.paragraphs if para.text])
                else:
                    with open(file_path, 'r', encoding='utf-8') as f:
                        raw_text = f.read()
                
                # 处理文档
                processed_content = self.splitter.process_document(raw_text)
                self.all_results[file_path] = processed_content
                
                # 更新当前显示
                self.current_file_path = file_path
                self.original_text.delete(1.0, tk.END)
                self.original_text.insert(tk.END, raw_text)
                self.processed_text.delete(1.0, tk.END)
                self.processed_text.insert(tk.END, processed_content)
                
            except Exception as e:
                messagebox.showerror("错误", f"处理文件 {file_path} 失败:\n{str(e)}")
        
        # 处理完成
        self.processing = False
        if hasattr(self, 'progress'):
            self.progress['value'] = 100
        self.progress_label.config(text=f"处理完成 {len(self.all_results)}/{self.total_files} 个文件")
        self.status_var.set(f"批量处理完成，共处理 {len(self.all_results)} 个文件")
        
        # 恢复UI状态
        if hasattr(self, 'open_btn'):
            self.open_btn.config(state=tk.NORMAL)
        if hasattr(self, 'batch_btn'):
            self.batch_btn.config(state=tk.NORMAL)
        if hasattr(self, 'process_btn'):
            self.process_btn.config(state=tk.NORMAL)
        if hasattr(self, 'batch_process_btn'):
            self.batch_process_btn.config(state=tk.NORMAL)
        if hasattr(self, 'stop_btn'):
            self.stop_btn.config(state=tk.DISABLED)
        
        # 如果有结果，启用导出按钮
        if self.all_results:
            if hasattr(self, 'export_btn'):
                self.export_btn.config(state=tk.NORMAL)
            if hasattr(self, 'export_all_btn'):
                self.export_all_btn.config(state=tk.NORMAL)
    
    def stop_processing(self):
        """停止批量处理"""
        self.processing = False
        self.status_var.set("处理已停止")
        if hasattr(self, 'progress_label'):
            self.progress_label.config(text="已停止")
    
    def export_current_document(self):
        """导出当前显示的文档"""
        if not self.processed_content:
            messagebox.showwarning("警告", "没有可导出的内容")
            return
        
        format_type = self.export_format.get()
        default_ext = f".{format_type}"
        default_name = os.path.splitext(os.path.basename(self.current_file_path))[0] + "_分块" + default_ext
        
        save_path = filedialog.asksaveasfilename(
            title="保存处理结果",
            defaultextension=default_ext,
            initialfile=default_name,
            filetypes=[(f"{format_type.upper()}文件", f"*.{format_type}"), ("所有文件", "*.*")]
        )
        
        if save_path:
            try:
                if format_type == 'docx':
                    self.export_to_word(self.processed_content, save_path)
                elif format_type == 'pdf':
                    self.export_to_pdf(self.processed_content, save_path)
                else:
                    with open(save_path, 'w', encoding='utf-8') as f:
                        f.write(self.processed_content)
                
                self.status_var.set(f"文档已导出到: {save_path}")
                messagebox.showinfo("成功", "文档导出成功")
            except Exception as e:
                messagebox.showerror("错误", f"导出失败:\n{str(e)}")
                self.status_var.set("导出失败")
    
    def export_all_results(self):
        """导出所有处理结果"""
        if not self.all_results:
            messagebox.showwarning("警告", "没有可导出的结果")
            return
        
        # 选择输出目录
        output_dir = filedialog.askdirectory(title="选择输出目录")
        if not output_dir:
            return
        
        format_type = self.export_format.get()
        total = len(self.all_results)
        success = 0
        
        # 显示导出进度
        self.progress['value'] = 0
        self.progress_label.config(text="准备导出...")
        self.status_var.set(f"准备导出 {total} 个文件")
        self.root.update()
        
        for i, (file_path, content) in enumerate(self.all_results.items(), 1):
            try:
                base_name = os.path.splitext(os.path.basename(file_path))[0]
                
                # 更新进度
                progress = int((i / total) * 100)
                self.progress['value'] = progress
                self.progress_label.config(text=f"正在导出 {i}/{total}: {base_name}")
                self.status_var.set(f"正在导出 {i}/{total}...")
                self.root.update()
                
                # 导出文档
                output_path = os.path.join(output_dir, f"{base_name}_分块.{format_type}")
                if format_type == 'docx':
                    self.export_to_word(content, output_path)
                elif format_type == 'pdf':
                    self.export_to_pdf(content, output_path)
                else:
                    with open(output_path, 'w', encoding='utf-8') as f:
                        f.write(content)
                
                success += 1
            except Exception as e:
                messagebox.showerror("错误", f"导出文件 {file_path} 失败:\n{str(e)}")
        
        # 导出完成
        self.progress['value'] = 100
        self.progress_label.config(text=f"导出完成 {success}/{total} 个文件")
        self.status_var.set(f"导出完成，成功 {success} 个，失败 {total-success} 个")
        messagebox.showinfo("完成", f"导出完成，成功 {success} 个，失败 {total-success} 个")
    
    def export_to_word(self, content: str, save_path: str):
        """导出为Word文档"""
        doc = Document()
        
        # 设置文档样式
        doc.styles['Normal'].font.name = 'Microsoft YaHei'
        doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), 'Microsoft YaHei')
        doc.styles['Normal'].font.size = Pt(10.5)
        
        # 设置页面边距（单位为英寸）
        sections = doc.sections
        for section in sections:
            section.left_margin = 914400  # 1英寸 = 914400 EMU
            section.right_margin = 914400
            section.top_margin = 914400
            section.bottom_margin = 914400
        
        # 按块处理内容
        chunks = content.split('\n\n')
        for chunk in chunks:
            if not chunk.strip():
                continue
                
            lines = chunk.split('\n')
            # 创建一个段落来包含整个块
            p = doc.add_paragraph()
            
            # 遍历块中的每一行
            for i, line in enumerate(lines):
                # 判断行的类型并设置相应的样式
                if i == 0:  # 第一行可能是标题
                    run = p.add_run(line)
                    # 根据内容特征设置样式
                    if re.match(r'^[一二三四五六七八九十]+[、：:].+$', line):
                        run.font.size = Pt(14)
                        run.font.bold = True
                    elif re.match(r'^\d+[、：:].+$', line) or re.match(r'^\d+\.\d+.*$', line):
                        run.font.size = Pt(12)
                        run.font.bold = True
                    elif re.match(r'^【.+】.*$', line) or re.match(r'^※.*$', line):
                        run.font.bold = True
                        run.font.color.rgb = RGBColor(0, 0, 128)  # 深蓝色
                    
                    # 如果不是块中的唯一一行，添加换行符
                    if i < len(lines) - 1:
                        p.add_run('\n')
                else:
                    # 处理普通文本行
                    run = p.add_run(line)
                    
                    # 为特殊的行设置样式
                    if re.match(r'^[•▪·]+[\s]*.*$', line) or re.match(r'^-[\s]+.*$', line):
                        run.font.color.rgb = RGBColor(64, 64, 64)  # 深灰色
                        
                    # 如果不是最后一行，添加换行符
                    if i < len(lines) - 1:
                        p.add_run('\n')
            
            # 块之间添加空白段落作为分隔
            doc.add_paragraph()
        
        # 保存文档
        doc.save(save_path)
    
    def export_to_pdf(self, content: str, save_path: str):
        """导出为PDF文件"""
        try:
            pdf = FPDF()
            pdf.add_page()
            
            # 尝试添加中文字体，如果失败则使用默认字体
            try:
                pdf.add_font('YaHei', '', 'msyh.ttf', uni=True)
                base_font = 'YaHei'
            except Exception:
                # 如果找不到微软雅黑，尝试使用其他常见中文字体
                try:
                    pdf.add_font('SimSun', '', 'simsun.ttc', uni=True) 
                    base_font = 'SimSun'
                except Exception:
                    # 如果都找不到，使用内置字体
                    base_font = 'Arial'
                    pdf.add_font('Arial', '', '', uni=True)
            
            # 设置默认字体
            pdf.set_font(base_font, '', 10)
            
            # 处理每个块
            chunks = content.split('\n\n')
            for chunk in chunks:
                if not chunk.strip():
                    continue
                    
                # 处理块中的每一行
                lines = chunk.split('\n')
                for i, line in enumerate(lines):
                    if not line.strip():
                        pdf.ln(5)  # 空行
                        continue
                        
                    # 根据行的特征设置样式
                    if i == 0:  # 第一行可能是标题
                        if re.match(r'^[一二三四五六七八九十]+[、：:].+$', line):
                            pdf.set_font(base_font, 'B', 14)
                            pdf.cell(0, 10, line, ln=1)
                        elif re.match(r'^\d+[、：:].+$', line) or re.match(r'^\d+\.\d+.*$', line):
                            pdf.set_font(base_font, 'B', 12)
                            pdf.cell(0, 8, line, ln=1)
                        elif re.match(r'^【.+】.*$', line) or re.match(r'^※.*$', line):
                            pdf.set_font(base_font, 'B', 11)
                            pdf.cell(0, 8, line, ln=1)
                        else:
                            pdf.set_font(base_font, '', 10)
                            pdf.multi_cell(0, 6, line)
                    else:
                        # 处理普通文本行
                        pdf.set_font(base_font, '', 10)
                        pdf.multi_cell(0, 6, line)
                
                # 块之间添加空行作为分隔
                pdf.ln(5)
            
            pdf.output(save_path)
        except Exception as e:
            raise Exception(f"PDF导出失败: {str(e)}。请检查字体是否安装或选择其他导出格式。")
            
    def _count_tokens(self, text):
        """估算文本中的标记数量（用于分块时参考）"""
        # 简单估算：汉字和标点算1个token，英文单词算1个token
        return len(re.findall(r'[\u4e00-\u9fff]|[，。！？；：""''（）【】《》、]|[a-zA-Z]+', text))

    def preview_heading_numbering(self):
        """预览标题识别效果"""
        if not self.current_file_path:
            messagebox.showwarning("警告", "没有可预览的文件")
            return
        
        raw_text = self.original_text.get(1.0, tk.END)
        if not raw_text.strip():
            messagebox.showwarning("警告", "没有可预览的文档内容")
            return
        
        try:
            # 显示预览中状态
            self.status_var.set("正在预览标题识别效果...")
            self.progress_label.config(text="预览中...")
            self.root.update()
            
            # 预览标题识别效果
            preview_text = self.splitter.identify_and_number_headings(raw_text)
            
            # 显示预览结果
            self.processed_text.delete(1.0, tk.END)
            self.processed_text.insert(tk.END, preview_text)
            
            self.status_var.set("预览完成")
            self.progress_label.config(text="预览完成")
        except Exception as e:
            messagebox.showerror("错误", f"预览失败:\n{str(e)}")
            self.status_var.set("预览失败")
            self.progress_label.config(text="预览失败")

if __name__ == "__main__":
    root = tk.Tk()
    app = DocumentProcessorApp(root)
    root.mainloop()